package com.wpre.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.wpre.lotterysystem.common.exception.ServiceException;
import com.wpre.lotterysystem.common.utils.JacksonUtil;
import com.wpre.lotterysystem.common.utils.MailUtil;
import com.wpre.lotterysystem.common.utils.SMSUtil;
import com.wpre.lotterysystem.controller.param.DrawPrizeParam;
import com.wpre.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.wpre.lotterysystem.dao.dataobject.WinningRecordDO;
import com.wpre.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.wpre.lotterysystem.dao.mapper.WinningRecordMapper;
import com.wpre.lotterysystem.service.DrawPrizeService;
import com.wpre.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.wpre.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.wpre.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityTiersEnum;
import com.wpre.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.wpre.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LianBao
 * Date: 2024-12-24
 * Time: 11:35
 */
@RabbitListener(queues = QUEUE_NAME) //监听者,监听的队列
@Component
@Slf4j
public class MQReceiver {

    @Autowired
    private DrawPrizeService drawPrizeService;

    @Autowired
    private ActivityStatusManager activityStatusManager;

    @Autowired
    private MailUtil mailUtil;
    @Autowired
    private SMSUtil smsUtil;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ThreadPoolTaskExecutor asyncServiceExecutor;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    /**
     * 处理消息
     *
     * @param message 发消息的类型
     */
    @RabbitHandler
    public void process(Map<String, String> message) {
        log.info("MQ接收到消息:{}", JacksonUtil.writeValueAsString(message));
        String paramString = message.get("messageData");
        DrawPrizeParam param = JacksonUtil.readValue(paramString, DrawPrizeParam.class);
        //处理抽奖流程
        try {

            //1.校验抽奖请求是否有效
            if (!drawPrizeService.checkParam(param)) {
                return;
            }

            //2.活动,奖品,人员状态处理(状态扭转处理)(加入了设计模式)
            statusConvert(param);//状态扭转

            //3.保存中奖者名单
            List<WinningRecordDO> winningRecordDOList = drawPrizeService.saveWinnerRecords(param);

            //4.通知中奖者(邮箱,短信)
            //后续流程,采用并发的方式
            sycnExecute(winningRecordDOList);


        } catch (ServiceException serviceException) {
            //如果发生异常,需要保证事务一致性(回滚),必须抛出一个异常


            log.error("{ }", serviceException);
            serviceException.printStackTrace();
            rollback(param);
            throw serviceException;
        } catch (Exception e) {
            //进行回滚操作
            log.error("{ }", e);
            e.printStackTrace();
            rollback(param);

        }


    }

    /**
     * 处理抽奖异常的回滚操作
     *
     * @param param
     */
    private void rollback(DrawPrizeParam param) {
        //1.回滚状态:活动 奖品 人员
        //判断状态是否需要回滚,如果不需要回滚就直接return
        if (!statusNeedRollback(param)) {
            //不需要回滚
            return;
        }
        rollbackStatus(param);
        //2.回滚中奖者名单
        if (!winnerNeedRollback(param)) {
            //中奖名单不需要回滚
            return;
        }
        rollbackWinner(param);

    }

    private boolean winnerNeedRollback(DrawPrizeParam param) {
        //查询这个活动下的奖品下有没有中奖名单
        int count = winningRecordMapper.countByAPId(param.getActivityId(), param.getPrizeId());
        return count > 0;//如果大于0,存在中奖者,需要回滚
    }

    /**
     * 回滚中奖记录
     *
     * @param param
     */
    private void rollbackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }


    private boolean statusNeedRollback(DrawPrizeParam param) {
        //判断活动+奖品+人员相关状态是否已经扭转
        //由于扭转状态时,保证了事务一致性,人员和奖品要么都扭转了,要么都没扭转
        //只需判断人员或奖品是否扭转过,就能判断状态是否全部扭转(不能去判断活动)
        ActivityPrizeDO activityPrizeDO =
                activityPrizeMapper.selectByAidPid(param.getActivityId(), param.getPrizeId());
        return activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.DONE.name());
    }

    private void rollbackStatus(DrawPrizeParam param) {
        //恢复相关的状态,使用
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setIds(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId).collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserStatus(ActivityUserStatusEnum.INIT);

        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);

    }


    /**
     * 并发处理抽奖后续流程
     *
     * @param winningRecordDOList
     */
    private void sycnExecute(List<WinningRecordDO> winningRecordDOList) {
        //线程池处理异步任务
        //1.短信通知
        asyncServiceExecutor.execute(() -> sendMessage(winningRecordDOList));
        //2.邮箱通知
        asyncServiceExecutor.execute(() -> sendMail(winningRecordDOList));

    }

    /**
     * 发邮件
     *
     * @param winningRecordDOList
     */
    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if (winningRecordDOList == null) {
            return;
        }
        for (WinningRecordDO winningRecordDO : winningRecordDOList) {
            String context = "Hi，" + winningRecordDO.getWinnerName() + "~ 恭喜你在"
                    + winningRecordDO.getActivityName() + "活动中获得"
                    + ActivityTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                    + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";
            mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),
                    "中奖通知", context);
        }

    }

    /**
     * 发短信
     *
     * @param winningRecordDOList
     */
    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
        if (winningRecordDOList == null) {
            return;
        }
        for (WinningRecordDO winningRecordDO : winningRecordDOList) {

            Map<String, String> map = new HashMap<>();
            map.put("name", winningRecordDO.getWinnerName());
            map.put("winningTime", DateUtil.formatTime(winningRecordDO.getWinningTime()));
            map.put("activityName", winningRecordDO.getActivityName());
            map.put("prizeTiers", winningRecordDO.getPrizeTier());
            map.put("prizeName", winningRecordDO.getPrizeName());

            smsUtil.sendMessage("SMS_478075170",
                    winningRecordDO.getWinnerPhoneNumber().getValue(),
                    JacksonUtil.writeValueAsString(map));
        }


    }

    /**
     * 状态扭转
     *
     * @param param
     */
    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.DONE);
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.DONE);
        convertActivityStatusDTO.setUserStatus(ActivityUserStatusEnum.DONE);

        activityStatusManager.handlerEvent(convertActivityStatusDTO);
    }
//    private void statusConvert(DrawPrizeParam param) {
//
//
//        //问题:活动扭转状态有依赖性,活动状态需要根据奖品状态来判断,并且扭转状态条件可能会扩展,当前写法维护性差
//        //解决方案:设计模式:责任链模式+策略模式
//
//        //1.活动关联的奖品状态:INIT-->DONE
//        //查询出关联活动的奖品信息
//        //判断奖品是否符合扭转的状态:如果是INIT就扭转成DONE,如果是DONE就不用
//
//        //2.活动关联的人员状态:INIT-->DONE
//        //查询出关联人员的奖品信息
//        //判断人员是否符合扭转的状态:如果是INIT就扭转成DONE,如果是DONE就不用
//
//
//        //3.活动状态:RUNNING-->DONE    活动奖品全部抽完
//        //查询活动信息
//        //判断活动是否符合扭转的状态:如果是RUNNING,并且全部奖品抽完之后,就扭转成DONE
//
//
//
//        //4.更新活动完整信息的缓存
//
//    }

}
